﻿/// <reference path="embed/OpenSeadragon.pre.js" />

// Code originally from the MS Ajax Toolkit, parts modified
Array.prototype.add = function(array, item) {
    // Setting Array[Array.length] is faster than Array.push() for a single element.
    array[array.length] = item;
};

Array.prototype.clear = function(array) {
    array.length = 0;
};

Array.prototype.clone = function(array) {
    return array.length === 1 ? [array[0]] : Array.apply(null, array);
};
SArray = Array();

// Function
if (!window.Function) {
    window.Function = {};
}
Function.prototype.createDelegate = function (object, method) {
    return function () {
        if (arguments === undefined)
            arguments = [];
        return method.apply(object, arguments);
    };
};

// String
function String() {

}
String.format = function (format, args) {
    return String._toFormattedString(false, arguments);
};
String._toFormattedString = function (useLocale, args) {
    var result = '';
    var format = args[0];

    for (var i = 0; ; ) {
        // Find the next opening or closing brace
        var open = format.indexOf('{', i);
        var close = format.indexOf('}', i);
        if ((open < 0) && (close < 0)) {
            // Not found: copy the end of the string and break
            result += format.slice(i);
            break;
        }
        if ((close > 0) && ((close < open) || (open < 0))) {
            //#if DEBUG
            // Closing brace before opening is an error
            if (format.charAt(close + 1) !== '}') {
                throw Error.argument('format', Sys.Res.stringFormatBraceMismatch);
            }
            //#endif
            result += format.slice(i, close + 1);
            i = close + 2;
            continue;
        }

        // Copy the string before the brace
        result += format.slice(i, open);
        i = open + 1;

        // Check for double braces (which display as one and are not arguments)
        if (format.charAt(i) === '{') {
            result += '{';
            i++;
            continue;
        }

        //#if DEBUG
        // at this point we have a valid opening brace, which should be matched by a closing brace.
        if (close < 0) throw Error.argument('format', Sys.Res.stringFormatBraceMismatch);
        //#else
        // This test is just done to break a potential infinite loop for invalid format strings.
        // The code here is minimal because this is an error condition in debug mode anyway.
        if (close < 0) break;
        //#endif

        // Find the closing brace

        // Get the string between the braces, and split it around the ':' (if any)
        var brace = format.substring(i, close);
        var colonIndex = brace.indexOf(':');
        var argNumber = parseInt((colonIndex < 0) ? brace : brace.substring(0, colonIndex), 10) + 1;
        //#if DEBUG
        if (isNaN(argNumber)) throw Error.argument('format', Sys.Res.stringFormatInvalid);
        //#endif
        var argFormat = (colonIndex < 0) ? '' : brace.substring(colonIndex + 1);

        var arg = args[argNumber];
        if (typeof (arg) === "undefined" || arg === null) {
            arg = '';
        }

        // If it has a toFormattedString method, call it.  Otherwise, call toString()
        if (arg.toFormattedString) {
            result += arg.toFormattedString(argFormat);
        }
        else if (useLocale && arg.localeFormat) {
            result += arg.localeFormat(argFormat);
        }
        else if (arg.format) {
            result += arg.format(argFormat);
        }
        else
            result += arg.toString();

        i = close + 1;
    }

    return result;
};

function Observer() { }
Observer.prototype = {
    _getContext: function (obj, create) {
        var ctx = obj._observerContext;
        if (ctx) return ctx();
        if (create) {
            return (obj._observerContext = this._createContext())();
        }
        return null;
    },
    _createContext: function () {
        // instead of attaching an EventHandlerList, etc directly onto the observed object, we attach a function
        // which returns it as a closure. This prevents the need to attach a field to the object which is not a 
        // function, which could have a negative impact on serializers, whereas they typically always skip functions.
        // It's also better for sparse arrays, where a for-var-in loop need only ignore functions.
        // Return an object instead of the handler list directly so we have a place to put other fields without having
        // to add separate getX() methods for each.
        var ctx = {
            events: new EventHandlerList()
        };
        return function () {
            return ctx;
        };
    }
};

var EventHandlerList = function EventHandlerList() {
    this._list = {};
};
EventHandlerList.prototype = {
    _addHandler: function (id, handler) {
        SArray.add(this._getEvent(id, true), handler);
    },
    addHandler: function (id, handler) {
        this._addHandler(id, handler);
    },
    _removeHandler: function (id, handler) {
        var evt = this._getEvent(id);
        if (!evt) return;
        SArray.remove(evt, handler);
    },
    _removeHandlers: function (id) {
        if (!id) {
            this._list = {};
        }
        else {
            var evt = this._getEvent(id);
            if (!evt) return;
            evt.length = 0;
        }
    },
    removeHandler: function (id, handler) {
        this._removeHandler(id, handler);
    },
    getHandler: function (id) {
        var evt = this._getEvent(id);
        if (!evt || !evt.length) return null;
        evt = SArray.clone(evt);
        return function (source, args) {
            for (var i = 0, l = evt.length; i < l; i++) {
                evt[i](source, args);
            }
        };
    },
    _getEvent: function (id, create) {
        var e = this._list[id];
        if (!e) {
            if (!create) return null;
            this._list[id] = e = [];
        }
        return e;
    }
};